Skip to content

fix(common): make SlickDataView filtering CSP-safe by default - #2774

Merged
ghiscoding merged 1 commit into
masterfrom
perf/dataview-csp
Aug 28, 2026
Merged

fix(common): make SlickDataView filtering CSP-safe by default#2774
ghiscoding merged 1 commit into
masterfrom
perf/dataview-csp

Conversation

@ghiscoding

@ghiscoding ghiscoding commented Aug 27, 2026

Copy link
Copy Markdown
Owner

Summary

Make SlickDataView filtering and accumulator compilation CSP-safe by default by removing runtime code generation through new Function().

The previous CSP-safe and non-CSP implementations are consolidated into a single callback-based implementation. The existing inlineFilters and useCSPSafeFilter options remain accepted as deprecated no-ops for backward compatibility.

Why

The generated filter and accumulator implementations require CSP policies to allow unsafe-eval. This prevents applications using a strict Content Security Policy from using every DataView configuration safely.

Maintaining separate CSP and non-CSP implementations also duplicates filtering, caching, accumulator, and dispatch logic.

Benchmarking shows that the unified CSP-safe implementation has equivalent or better performance for the default configuration. However, the explicitly enabled generated Function path remains faster for string-heavy predicates. This trade-off is documented in the performance analysis below.

Changes

  • Removed filter compilation through new Function().
  • Removed the generated accumulator implementation.
  • Consolidated filtering into the existing CSP-safe callback loops.
  • Simplified setFilter(), getFilter(), filtering dispatch, and accumulator setup.
  • Corrected the CSP-safe expanding-filter cache so successful matches are stored.
  • Deprecated inlineFilters and useCSPSafeFilter; both remain accepted but are ignored.
  • Deprecated the unused FilterCspFn and FilterWithCspCachingFn aliases.
  • Added a next-major cleanup checklist to the deprecated API comments.
  • Added unit coverage confirming:
    • filtering never invokes Function;
    • deprecated options remain accepted;
    • filtering and filter arguments remain compatible;
    • expanding-filter caching stores successful matches.
  • Added an isolated Vitest benchmark for the production filter and accumulator loops.
  • Added documentation explaining how to capture and compare benchmark baselines.
  • Updated CSP documentation for Angular, Aurelia, React, Vue, and the shared documentation.
  • Removed the now-unnecessary CSP option from the vanilla demo.

Performance analysis

The exact pre-change implementation was loaded alongside the updated implementation in the same Node 24 process.

Each comparison filtered 100,000 items. Execution order was rotated across 12 samples to reduce warm-up, garbage-collection, and benchmark-order bias. The comparison was repeated three times.

The following values are the average median execution time. Lower is better.

Implementation Numeric/branch-heavy filter String-heavy filter
Before: default non-inline loop 0.943 ms 4.024 ms
Before: generated non-CSP Function 0.831 ms 3.516 ms
Before: CSP-safe callback loop 0.805 ms 3.977 ms
After: unified CSP-safe loop 0.788 ms 4.029 ms

Default configuration

Compared with the previous default inlineFilters: false path:

  • Numeric/branch-heavy filtering improved by approximately 16.5%.
  • String-heavy filtering changed by approximately 0.1%, which is effectively unchanged.
  • The accumulator loop improved by approximately 5–6%.

This is the relevant comparison for consumers who did not explicitly enable inlineFilters.

Previous CSP-safe path

Compared with the previous CSP-safe callback implementation:

  • Numeric filtering improved by approximately 2.1%.
  • String filtering was approximately 1.3% slower.

These differences are small enough to consider the implementations relatively equivalent.

Generated inline path

Compared with inlineFilters: true using generated Function code:

  • Numeric filtering improved by approximately 5.1%.
  • String-heavy filtering was approximately 14.6% slower.

The string predicate is the only tested scenario where generated runtime code showed a consistent advantage. This affects consumers who explicitly enabled inlineFilters; the option was disabled by default.

The absolute measured difference was approximately 0.51 ms per 100,000 items. This is small for an individual filtering pass but could become significant with very large datasets or frequent refreshes.

CSP-safe optimization investigation

Additional CSP-safe implementations were benchmarked to determine whether the generated string-filter performance could be preserved:

  • caching the predicate outside the loop;
  • indexed result writes instead of push();
  • result-array preallocation;
  • a specialized closure created when setting the filter;
  • native Array.filter();
  • for...of iteration.

None consistently matched the generated implementation without regressing another workload.

The generated path benefits from embedding the arbitrary predicate body directly inside the item loop. A generic CSP-safe implementation must retain a callback boundary, which JavaScript engines cannot be relied upon to inline for every predicate.

Caching the predicate occasionally reduced the string-filter regression from approximately 16% to 12%, but the improvement was inconsistent and could reduce numeric-filter performance. Indexed writes did not provide a consistent improvement, while preallocation, Array.filter(), and for...of were slower.

Performance conclusion

The performance trade-off is explicit:

  • Default users receive equivalent or improved filtering performance.
  • Accumulator performance improves.
  • Numeric filtering improves across the tested implementations.
  • Consumers using the deprecated inlineFilters: true option with string-heavy predicates may experience a 12–16% filtering-loop regression.

This regression is accepted in exchange for unconditional CSP compatibility, removal of runtime code generation, and a simpler single implementation.

Backward compatibility

The inlineFilters and useCSPSafeFilter properties remain part of DataViewOption, so existing applications continue to compile without configuration changes.

Both properties are deprecated and ignored because filtering is now always CSP-safe. Filtering results and filter-argument behavior remain compatible.

The behavioral difference is limited to the performance characteristics of the previous inlineFilters: true generated-code path.

Next major release

A cleanup checklist was added directly to the deprecated API comments. The next major release can:

  • remove inlineFilters and useCSPSafeFilter from DataViewOption and its defaults;
  • remove framework and vanilla-bundle code that forwards inlineFilters;
  • remove the corresponding compatibility tests and deprecated documentation;
  • remove the deprecated FilterCspFn and FilterWithCspCachingFn aliases;
  • optionally rename protected *CSPSafe methods to neutral names and update their tests and benchmarks.

The unsafe implementations are already removed by this change, so no additional generated filtering or accumulator implementation will need to be removed later.

Validation

  • pnpm test — 5,965 tests passed.
  • pnpm lint — passed.
  • pnpm prettier:check — passed.
  • pnpm build — passed.
  • pnpm bench:data-view — benchmark completed successfully.
  • git diff --check — passed.
  • Scoped coverage reports 100% statements, functions, and lines for the changed production logic.
  • Whole-file SlickDataView branch coverage remains 90.83% because of unrelated existing branches.

Comments

The benchmark intentionally isolates filter and accumulator loops from paging, grouping, events, and row-difference calculations. Absolute browser results may differ by JavaScript engine, but the benchmark provides a repeatable comparison of the affected code paths.

The benchmark can be run with:

pnpm bench:data-view

A baseline can be saved and compared across revisions with:

pnpm bench:data-view --outputJson /tmp/slick-dataview-before.json
pnpm bench:data-view --compare /tmp/slick-dataview-before.json

Results should be repeated on an otherwise idle machine. Differences smaller than the reported relative margin of error should be treated as inconclusive.

AI / LLM assistance

  • AI / LLM assistance used:
    • No
    • Yes
  • If Yes:
    • which tool/model: OpenAI Codex GPT-5.6 Sol
    • how was it used: Code analysis, implementation assistance, test and benchmark creation, performance comparison, alternative optimization analysis, documentation updates, and change review.

Checklist

  • The changes are limited to one scope. The additional tests, benchmarks, demo change, and documentation support the DataView CSP change.
  • Tests were added or updated where appropriate.
  • Documentation was updated where appropriate.

@ghiscoding ghiscoding changed the title refactor(common): make SlickDataView filtering CSP-safe perf(common): make SlickDataView filtering CSP-safe by default Aug 27, 2026
@pkg-pr-new

pkg-pr-new Bot commented Aug 27, 2026

Copy link
Copy Markdown
angular-slickgrid

npm i https://pkg.pr.new/angular-slickgrid@2774

aurelia-slickgrid

npm i https://pkg.pr.new/aurelia-slickgrid@2774

slickgrid-react

npm i https://pkg.pr.new/slickgrid-react@2774

slickgrid-vue

npm i https://pkg.pr.new/slickgrid-vue@2774

@slickgrid-universal/angular-row-detail-plugin

npm i https://pkg.pr.new/@slickgrid-universal/angular-row-detail-plugin@2774

@slickgrid-universal/aurelia-row-detail-plugin

npm i https://pkg.pr.new/@slickgrid-universal/aurelia-row-detail-plugin@2774

@slickgrid-universal/react-row-detail-plugin

npm i https://pkg.pr.new/@slickgrid-universal/react-row-detail-plugin@2774

@slickgrid-universal/vue-row-detail-plugin

npm i https://pkg.pr.new/@slickgrid-universal/vue-row-detail-plugin@2774

@slickgrid-universal/binding

npm i https://pkg.pr.new/@slickgrid-universal/binding@2774

@slickgrid-universal/common

npm i https://pkg.pr.new/@slickgrid-universal/common@2774

@slickgrid-universal/composite-editor-component

npm i https://pkg.pr.new/@slickgrid-universal/composite-editor-component@2774

@slickgrid-universal/custom-footer-component

npm i https://pkg.pr.new/@slickgrid-universal/custom-footer-component@2774

@slickgrid-universal/custom-tooltip-plugin

npm i https://pkg.pr.new/@slickgrid-universal/custom-tooltip-plugin@2774

@slickgrid-universal/empty-warning-component

npm i https://pkg.pr.new/@slickgrid-universal/empty-warning-component@2774

@slickgrid-universal/event-pub-sub

npm i https://pkg.pr.new/@slickgrid-universal/event-pub-sub@2774

@slickgrid-universal/excel-export

npm i https://pkg.pr.new/@slickgrid-universal/excel-export@2774

@slickgrid-universal/graphql

npm i https://pkg.pr.new/@slickgrid-universal/graphql@2774

@slickgrid-universal/odata

npm i https://pkg.pr.new/@slickgrid-universal/odata@2774

@slickgrid-universal/pagination-component

npm i https://pkg.pr.new/@slickgrid-universal/pagination-component@2774

@slickgrid-universal/pdf-export

npm i https://pkg.pr.new/@slickgrid-universal/pdf-export@2774

@slickgrid-universal/row-detail-view-plugin

npm i https://pkg.pr.new/@slickgrid-universal/row-detail-view-plugin@2774

@slickgrid-universal/rxjs-observable

npm i https://pkg.pr.new/@slickgrid-universal/rxjs-observable@2774

@slickgrid-universal/sql

npm i https://pkg.pr.new/@slickgrid-universal/sql@2774

@slickgrid-universal/text-export

npm i https://pkg.pr.new/@slickgrid-universal/text-export@2774

@slickgrid-universal/utils

npm i https://pkg.pr.new/@slickgrid-universal/utils@2774

@slickgrid-universal/vanilla-bundle

npm i https://pkg.pr.new/@slickgrid-universal/vanilla-bundle@2774

@slickgrid-universal/vanilla-force-bundle

npm i https://pkg.pr.new/@slickgrid-universal/vanilla-force-bundle@2774

@slickgrid-universal/web-mcp

npm i https://pkg.pr.new/@slickgrid-universal/web-mcp@2774

commit: ca6cc12

@codecov

codecov Bot commented Aug 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.0%. Comparing base (aef1507) to head (ca6cc12).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #2774   +/-   ##
=======================================
  Coverage   100.0%   100.0%           
=======================================
  Files         199      199           
  Lines       25832    25763   -69     
  Branches     9142     9128   -14     
=======================================
- Hits        25832    25763   -69     
Flag Coverage Δ
angular 100.0% <ø> (ø)
universal 100.0% <100.0%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@ghiscoding ghiscoding changed the title perf(common): make SlickDataView filtering CSP-safe by default fix(common): make SlickDataView filtering CSP-safe by default Aug 28, 2026
@ghiscoding
ghiscoding merged commit 2489bbc into master Aug 28, 2026
26 checks passed
@ghiscoding
ghiscoding deleted the perf/dataview-csp branch August 28, 2026 00:41
@github-actions

Copy link
Copy Markdown

🎉 This pull request is included in version 10.10.0 📦
🔗 The release notes are available at: GitHub Release 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant